import { Regexp_Utils } from "./validate";

/**
 * 常用工具方法
 */
declare const uni:any;
declare const navigateTo:Function;

// 手机号码掩码处理
export const coverPhone = (phone: string, option: { prefix?: string, needPrefix?: boolean } = { prefix: '+86 ', needPrefix: false }) => {
    if (Regexp_Utils.phone.test(phone)) {
        const coveredPhone = phone.slice(0, 3) + '*'.repeat(4) + phone.slice(-4);
        return option.needPrefix ? option.prefix + coveredPhone : coveredPhone;
    }
    return `不合法手机号`;
}


/**
 * 获取随机数，如果是浏览器环境可以使用 window.Crypto.getRandomValues(), 参考 https://developer.mozilla.org/zh-CN/docs/Web/API/Crypto/getRandomValues
 * @param len 随机数的位数
 * @returns 获取随机数
 */
export const getRandomNumber = (len: number = 5) => Math.floor(Math.random() * Math.pow(10, len));


/**
 * 根据时间戳+随机数获取随机字符串，也可以考虑使用Symbol
 * @param len 随机数长度
 * @param seprator 分割符号
 * @returns 随机字符串
 */
export const getRandomDateString = (len: number = 3, seprator: string = '_') => `${Date.now()}${seprator}${getRandomNumber(len)}`;


/**
 * 页面跳转到登录页
 * @param loginSuccessPath 登录成功后的跳转路径
 * @returns Promise
 * **/
export function toLoginPage(loginSuccessPath?: string) {
    // @to-do: 获取当前页的url或route对象
    const currentUrl: any = '获取当前页的url';
    // 如果当前页面为登录页，则直接返回
    if (currentUrl.indexOf('/login') > -1) {
        return;
    }
    // @to-do: 获取登录后的跳转页，没有就是当前页
    const from = loginSuccessPath ? encodeURIComponent(loginSuccessPath) : encodeURIComponent(currentUrl);

    // @to-do： 跳转登录页，各个平台不一致
    return navigateTo({
        path: '/login',
        query: {
            from,
        }
    })
}

/**
 * 节流函数，用于限制某个函数在指定时间内的调用频率。
 * 
 * @param func - 需要被节流的函数。
 * @param wait - 节流的时间间隔，单位为毫秒，默认为 300 毫秒。
 * @returns 返回一个新的函数，该函数在指定时间内只会执行一次。
 */
export function throttleFunction<T extends (...args: any[]) => any>(func: T, wait?: number) {
    let waitTime = wait || 300;
    let timer: number | undefined = undefined;
    return function (this: any, ...args: any[]) {
        if (!timer) {
            func.apply(this, args); // 先执行, 写法1
            timer = setTimeout(() => {
                // func.apply(this, args); // 后执行, 写法2
                clearTimeout(timer);
                timer = undefined;
            }, waitTime);
        }
    } as T;
}

/**
 * 防抖函数，用于限制某个函数在指定时间内的调用频率。
 * 
 * @param func - 需要防抖的函数。
 * @param wait - 防抖的时间间隔，单位为毫秒，默认为 300 毫秒。
 * @returns 返回一个新的防抖函数。
 * 
 * 使用示例：
 * const debouncedFunction = debounceFunction(() => {
 *     console.log('Function executed!');
 * }, 500);
 */
export function debounceFunction<T extends (...args: any[]) => any>(func: T, wait?: number) {
    let waitTime = wait || 300;
    let timer: number | undefined = undefined;
    return function (this: any, ...args: any[]) {
        clearTimeout(timer);
        timer = undefined;
        timer = setTimeout(() => {
            func.apply(this, args);
            clearTimeout(timer);
            timer = undefined;
        }, waitTime);
    } as T;
}


/**
 * 深度复制对象
 * @param {*} source 深度拷贝的目标对象
 * @returns 完成拷贝的对象
 */
export const deepCopy = (source: any, cache = new Map()): any => {
    if (!source || !(source instanceof Object) || source instanceof Function) {
        return source;
    }

    // 防止循环引用
    if (cache.has(source)) {
        return cache.get(source);
    }


    if (Array.isArray(source)) {
        const result = source.map(item => deepCopy(item, cache));
        cache.set(source, result);
        return result;
    }

    if (Object.prototype.toString.call(source) == '[object Date]') {
        return new Date(source.getTime());
    }

    let target = Object.create(Object.getPrototypeOf(source)); // 复制原型对象
    for (let key of Object.keys(source)) { // 复制本身
        target[key] = deepCopy(source[key], cache);
    }
    cache.set(source, target);

    return target;
}

/**
 * 深度合并两个对象，并返回目标对象; Object.assign 只合并一层
 * @param {Object} target 需要重新赋值的对象
 * @param {(Object|Array)} source 需要被合并的对象
 * @returns {Object} 合并后的对象
 */
export function mergeObject(target: Record<string, any>, source: Record<string, any>|Array<any>) {
  if (typeof target !== 'object'||target===null) {
      target = {}
  }
  if (Array.isArray(source)) {
      return source.slice()
  }
  Object.keys(source).forEach(property => {
      const sourcePropertyVal = source[property]
      if (typeof sourcePropertyVal === 'object'&&sourcePropertyVal!==null) {
          target[property] = mergeObject(target[property], sourcePropertyVal)
      } else {
          target[property] = sourcePropertyVal
      }
  })
  return target
}


/**
 * 缓存工具类，提供对localStorage本地存储的封装操作（@to-do： 其他平台可能要修改缓存的存储方式）。
 * @export
 * @class CacheUtils
 */
export const CacheUtils = {
    set(key: string, value: any, expireDate?: Date) {
        localStorage.setItem('_' + key, typeof value === 'string' ? value : JSON.stringify(value));
        if (expireDate) {
            localStorage.setItem('_' + key + '_expireDate', String(expireDate.getTime()));
        }
    },
    get(key: string) {
        if (localStorage.getItem('_' + key + '_expireDate')) {
            const expireDate = new Date(Number(localStorage.getItem('_' + key + '_expireDate')));
            if (expireDate.getTime() < new Date().getTime()) {
                CacheUtils.remove(key);
                return null;
            }
        }
        return  localStorage.getItem('_' + key)?JSON.parse(localStorage.getItem('_' + key)||'{}'):localStorage.getItem('_' + key);
    },

    remove(key: string) {
        localStorage.removeItem('_' + key);
        localStorage.removeItem('_' + key + '_expireDate');
    }
}



// Tab页面列表的url
const tabPages = [
    '/',
    '/pages/index/index',
    '/pages/schedule/index',
    '/pages/person/index',
]

/**
 * 导航到uniapp指定页面。
 * 如果目标页面已在标签页中，则切换到该标签页；否则，打开新页面。
 * @param url - 目标页面的路径。
 */
export const navigateToUniPage = (url: string) => {
    const matchIndex = tabPages.findIndex((el) => el == url.split('?')[0]);
    if (matchIndex!== -1) {
        if(matchIndex == 0) {
            return uni.switchTab({ url: tabPages[1], });
        }
        return uni.switchTab({ url, });
    }

    return uni.navigateTo({ url, })
}


/**
 * 日期格式化，甚牛而逼之
 * @param date - 要格式化的日期对象。
 * @param format - 日期样式。
 * @returns 格式化后的日期字符串。
 */
export const formatDate = (date: Date, format = 'YYYY-MM-DD HH:mm:ss') => {
    // padStart() 方法用另一个字符串填充当前字符串（如果需要会重复填充），直到达到给定的长度。填充是从当前字符串的开头开始的; 类似有padEnd()方法
    const pad = (num: number) => String(num).padStart(2, '0'); 

    return format
        .replace(/YYYY/g, String(date.getFullYear()))
        .replace(/MM/g, pad(date.getMonth() + 1))
        .replace(/DD/g, pad(date.getDate()))
        .replace(/HH/g, pad(date.getHours()))
        .replace(/mm/g, pad(date.getMinutes()))
        .replace(/ss/g, pad(date.getSeconds()))
        .trim();
}


/**
 * 将字符串转换为布尔值, 一般用于env配置文件的转换
 * @param str 需要转换的字符串,只能是 'true' 或 'false'
 * @returns 如果输入 'true' 返回 true,否则返回 false
 */
export function StringToBoolean(str: string) {
    if (str === 'true') {
        return true;
    } else if (str === 'false') {
        return false;
    }

    return false;
}


/**
 * url+参数编码，例如 encodeUrlwithArgs('pages/home/index', 'a',1, 'b, 2) => 'pages/home/index?a=1&b=2'
 * @param {*} url 主机url
 * @param  {...any} args 多个传入的参数
 */
export const encodeUrlwithArgs = (url:string, ...args:string[])=>{
    let resultUrl = url;
    for(let i=0;i<args.length;i+=2){
        resultUrl = String(resultUrl).indexOf('?')==-1 ? resultUrl+`?${args[i]}=${args[i+1]}`: resultUrl+`&${args[i]}=${args[i+1]}`;
    }
    return resultUrl;
}

/**
 * Url+对象后进行编码（不支持对象嵌套结构）
 * @param {*} url 主机url
 * @param {*} obj 编码的对象
 * @returns 编码后的url
 */
export const encodeUrlWithObj = (url:string,obj:Record<string,any>={})=>{
    let arr:string[] = [];
    for(let [key, val ] of Object.entries(obj)){
        arr.push(key);
        arr.push(val);
    }
    return encodeUrlwithArgs(url, ...arr);
}

/**
 * 返回模拟数据的 Promise，带有延迟。
 * 
 * @param data - 要返回的模拟数据。
 * @param delay - 延迟时间（毫秒），默认为 1000 毫秒。
 * @returns 返回一个 Promise，解析为传入的模拟数据。
 */
export function returnMockData<T>(data: any, delay: number = 1000) {
    return new Promise<T>((resolve) => setTimeout(resolve, delay, data));
}

